package rt2

import (
	"fmt"
	"net"

	"gitee.com/zhang-li17/rt2/pcol"

	"github.com/sirupsen/logrus"
	"google.golang.org/protobuf/proto"
)

type MultiCastClient struct {
	Conn *net.UDPConn
	Name string
	Dec  *Decoder
}

// connectServer 连接组播服务器
func connectServer(ip string, port int, name string) (*MultiCastClient, error) {
	// 1. 加入组播组
	multiCastAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", ip, port))
	if err != nil {
		logrus.Errorf("Error resolving multicast address: %v", err)
		return nil, err
	}

	// 2. 创建一个UDP连接并监听
	// 绑定到一个随机的本地端口
	localAddr, err := net.ResolveUDPAddr("udp", "0.0.0.0:0")
	if err != nil {
		logrus.Errorf("Error resolving local address: %v", err)
		return nil, err
	}

	conn, err := net.ListenUDP("udp", localAddr)
	if err != nil {
		logrus.Errorf("Error creating UDP connection: %v", err)
		return nil, err
	}
	// defer conn.Close()

	// 获取本地监听的端口
	localPort := conn.LocalAddr().(*net.UDPAddr).Port

	// 3. 发送ConnectRequest消息
	connectRequest := &pcol.ConnectRequest{
		Name:        name,
		Waiting:     true,
		UnicastPort: int32(localPort),
	}

	data, err := proto.Marshal(connectRequest)
	if err != nil {
		logrus.Errorf("Error marshaling ConnectRequest: %v", err)
		return nil, err
	}

	dec := NewDecoder(HEADER, BUF_SIZE)
	data = dec.PackAndEncode(FrameType(pcol.MessageType_CONNECT_REQUEST), data)

	_, err = conn.WriteToUDP(data, multiCastAddr)
	if err != nil {
		logrus.Errorf("Error sending ConnectRequest: %v", err)
		return nil, err
	}

	logrus.Infof("[1/4]组播请求连接成功: %s->%s, data: %v", conn.LocalAddr().String(), multiCastAddr.String(), connectRequest)

	// 4. 等待读取ConnectConfirm消息
	// 设置超时时间，防止无限等待
	// conn.SetReadDeadline(time.Now().Add(3 * time.Second))
	buffer := make([]byte, 1024)

	n, remoteAddr, err := conn.ReadFromUDP(buffer)
	if err != nil {
		if opErr, ok := err.(*net.OpError); ok && opErr.Timeout() {
			logrus.Error("Timeout waiting for ConnectConfirm")
			return nil, fmt.Errorf("timeout waiting for ConnectConfirm")
		}
		logrus.Errorf("Error reading ConnectConfirm: %v", err)
		return nil, err
	}

	// 解析ConnectConfirm消息
	confirm := &pcol.ConnectConfirm{}
	frames := dec.RecvAndDecode(buffer[:n])
	if len(frames) == 0 {
		logrus.Errorln("recv 0 valid frames")
	}
	err = proto.Unmarshal(frames[0].Payload, confirm)
	if err != nil {
		logrus.Errorf("Error unmarshaling ConnectConfirm: %v", err)
		return nil, err
	}

	logrus.Infof("[2/4]接收到服务器单播连接确认: %s->%s, data: %v", remoteAddr.String(), conn.LocalAddr().String(), confirm)

	// 验证确认消息的内容
	if confirm.LocalName != "Server" && confirm.RemoteName != name {
		logrus.Errorf("ConnectConfirm validate failed: fault name")
		return nil, fmt.Errorf("ConnectConfirm validate failed: fault name")
	}

	// 5. 回复同样的消息
	confirm.LocalName, confirm.RemoteName = confirm.RemoteName, confirm.LocalName
	data, err = proto.Marshal(confirm)
	if err != nil {
		logrus.Errorf("Error marshaling ConnectConfirm: %v", err)
		return nil, err
	}

	data = dec.PackAndEncode(FrameType(pcol.MessageType_CONNECT_CONFIRM), data)
	_, err = conn.WriteToUDP(data, remoteAddr)
	if err != nil {
		logrus.Errorf("Error sending ConnectConfirm: %v", err)
		return nil, err
	}

	logrus.Infof("[3/4]回复单播连接确认: %s->%s, data: %v", conn.LocalAddr().String(), remoteAddr.String(), confirm)

	// 在组播组中发送退出消息
	connectRequest.Waiting = false

	data, err = proto.Marshal(connectRequest)
	if err != nil {
		logrus.Errorf("Error marshaling ConnectRequest: %v", err)
		return nil, err
	}

	data = dec.PackAndEncode(FrameType(pcol.MessageType_CONNECT_REQUEST), data)

	go func() {
		_, err = conn.WriteToUDP(data, multiCastAddr)
		if err != nil {
			logrus.Errorf("Error sending ConnectRequest: %v", err)
			return
		}

		logrus.Infof("[4/4]组播请求连接结束: %s->%s, data: %v", conn.LocalAddr().String(), multiCastAddr.String(), connectRequest)
	}()

	return &MultiCastClient{
		Conn: conn,
		Name: name,
		Dec:  NewDecoder(HEADER, BUF_SIZE),
	}, nil
}
